/*
 * Copyright 2007 Ulf Ochsenfahrt <ulf@ofahrt.de>
 *
 * This file is part of mnemoj, a software transactional memory implementation
 * that is based on Project Darkstar.
 *
 * mnemoj is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation and
 * distributed hereunder to you.
 *
 * Project Darkstar Server is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.ofahrt.mnemoj.util;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.ManagedReference;

public final class ManagedArrayList<E extends ManagedObject> implements List<E>, Serializable
{

private static final long serialVersionUID = 1L;

private Class<E> type;
private int size = 0;
private ManagedReference[] data = new ManagedReference[10];

public ManagedArrayList(Class<E> type)
{
	this.type = type;
}

private void ensureCapacity(int value)
{
	if (data.length < value)
	{
		int newcapacity = Math.min(value, 2*data.length+1);
		ManagedReference[] newdata = new ManagedReference[newcapacity];
		System.arraycopy(data, 0, newdata, 0, size);
		data = newdata;
	}
}

public int size()
{ return size; }

public boolean isEmpty()
{ return size == 0; }

public boolean contains(Object o)
{
	if (!(o instanceof ManagedObject)) return false;
	DataManager manager = AppContext.getDataManager();
	ManagedReference ref = manager.createReference((ManagedObject) o);
	for (int i = 0; i < data.length; i++)
		if (ref.equals(data[i])) return true;
	return false;
}

public Iterator<E> iterator()
{
	return new Iterator<E>()
		{
			private int index = 0;
			@Override
			public boolean hasNext()
			{ return index < size; }
			@Override
			public E next()
			{
				if (index >= size) throw new NoSuchElementException();
				E result = data[index].get(type);
				index++;
				return result;
			}
			@Override
			public void remove()
			{
				throw new UnsupportedOperationException();
			}
		};
}

public Object[] toArray()
{
	Object[] result = new Object[size];
	for (int i = 0; i < size; i++)
		result[i] = data[i].get(type);
	return result;
}

public boolean add(E element)
{
	add(size, element);
	return true;
}

public boolean remove(Object o)
{
	if (!(o instanceof ManagedObject)) return false;
	DataManager manager = AppContext.getDataManager();
	ManagedReference ref = manager.createReference((ManagedObject) o);
	for (int i = 0; i < data.length; i++)
		if (ref.equals(data[i]))
		{
			remove(i);
			return true;
		}
	return false;
}

public boolean addAll(Collection<? extends E> c)
{
	boolean changed = false;
	for (E e : c)
	{
		add(e);
		changed = true;
	}
	return changed;
}

public void clear()
{
	data = new ManagedReference[10];
	size = 0;
}

// equals+hashCode

public E get(int index)
{ return data[index].get(type); }

public E set(int index, E element)
{
	if ((index < 0) || (index >= size)) throw new IndexOutOfBoundsException();
	E result = data[index].get(type);
	data[index] = AppContext.getDataManager().createReference(element);
	return result;
}

public void add(int index, E element)
{
	if ((index < 0) || (index > size)) throw new IndexOutOfBoundsException();
	ensureCapacity(size+1);
	if (index != size)
		System.arraycopy(data, index, data, index+1, size-index);
	data[index] = AppContext.getDataManager().createReference(element);
	size++;
}

public E remove(int index)
{
	if ((index < 0) || (index >= size)) throw new IndexOutOfBoundsException();
	E result = data[index].get(type);
	System.arraycopy(data, index+1, data, index, size-index-1);
	data[size] = null;
	size--;
	return result;
}

public ListIterator<E> listIterator()
{ return listIterator(0); }

public ListIterator<E> listIterator(final int startIndex)
{
	return new ListIterator<E>()
		{
			private int index = startIndex;
			private int lastReturned = -1;
			@Override
			public boolean hasNext()
			{ return index < size; }
			@Override
			public boolean hasPrevious()
			{ return index > 0; }
			@Override
			public E next()
			{
				if (index >= size) throw new NoSuchElementException();
				index++;
				lastReturned = index-1;
				return data[index-1].get(type);
			}
			@Override
			public E previous()
			{
				if (index <= 0) throw new NoSuchElementException();
				index--;
				lastReturned = index;
				return data[index].get(type);
			}
			@Override
			public int nextIndex()
			{ return index; }
			@Override
			public int previousIndex()
			{ return index-1; }
			@Override
			public void add(E e)
			{
				ManagedArrayList.this.add(index, e);
				index++;
				lastReturned = -1;
			}
			@Override
			public void set(E e)
			{
				ManagedArrayList.this.set(index, e);
				index++;
			}
			@Override
			public void remove()
			{
				if (lastReturned < 0) throw new IllegalStateException();
				ManagedArrayList.this.remove(lastReturned);
				if (lastReturned < index) index--;
				lastReturned = -1;
			}
		};
}


@Override
public boolean addAll(int index, Collection<? extends E> c)
{ throw new UnsupportedOperationException(); }

@Override
public boolean containsAll(Collection<?> c)
{ throw new UnsupportedOperationException(); }

@Override
public int indexOf(Object o)
{ throw new UnsupportedOperationException(); }

@Override
public int lastIndexOf(Object o)
{ throw new UnsupportedOperationException(); }

@Override
public boolean removeAll(Collection<?> c)
{ throw new UnsupportedOperationException(); }

@Override
public boolean retainAll(Collection<?> c)
{ throw new UnsupportedOperationException(); }

@Override
public List<E> subList(int fromIndex, int toIndex)
{ throw new UnsupportedOperationException(); }

@Override
public <T> T[] toArray(T[] a)
{ throw new UnsupportedOperationException(); }

}
